package com.zhiwang.planet.widget

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.os.Handler
import android.os.Message
import android.os.Parcelable
import android.text.TextUtils
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import com.zhiwang.planet.R
import com.zhiwang.planet.mvvm.model.entity.main.Diamond
import com.zhiwang.planet.util.Toaster
import com.zhiwang.planet.util.dp
import java.util.*
import kotlin.collections.ArrayList

/**
 * 采用的是随机数的方式随机显示view;但是如果只是想达到view位置的错落，并非是要view的位置随机，完全可以预置一些
 * 位置，然后每次随机取几个位置，一样可以达到view错落的效果，并且效率更高，避免检测是否重叠;本次实现使用的还是随机生成检测
 * 直到找到一定数量的彼此互不碰撞的位置点生成气泡
 */

val AMPLITUDE = 6
var EDGE = 20
val removalTag = Object()

class ShuffleLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
    : ViewGroup(context, attrs, defStyleAttr) {
    private val TAG = "ShuffleLayout"

//    private val itemSpace = 5

    private val horizontalSpace = 5

    private val verticalSpace = 15
    /**
     * 最多允许显示的view数量
     */
    private var maxCount = 0

    private var defaultEmptyHint: String = "趣砖正在生长"

    private val random = Random()

    /**
     * 保存已经添加view的信息，方便新生成的view的碰撞检测
     */
    private val views = ArrayList<ItemView>()


    var datas = ArrayList<Diamond>()

    val oldDatas = ArrayList<Diamond>()

    private var rest = ArrayList<Diamond>()

    private var isLooping = false

    private var fixMode = true

    init {
        val typeArray = context.obtainStyledAttributes(attrs, R.styleable.ShuffleLayout)
        maxCount = typeArray.getInt(R.styleable.ShuffleLayout_max_count, 6)
        defaultEmptyHint = typeArray.getString(R.styleable.ShuffleLayout_empty_hint) ?: "趣钻正在生长"
        typeArray.recycle()
    }

    /**
     * 延迟加载Handler
     */
    private val mHandler: H = H()

    /**
     * Handler更新每一帧的数据。必须使用Handler，大批量的动画想都不用想是低效率的
     */
    private inner class H : Handler() {
        //        private val weakRef = WeakReference<ShuffleLayout>(shuffleLayout)
        private val INTERVAL: Long = 70// 1000 ÷ 16 = 62

        override fun handleMessage(msg: Message?) {
            if ((msg?.what ?: 0) == 0) {
                update()
                sendEmptyMessageDelayed(0, INTERVAL)
            }
        }

        private fun update() {
            val count = childCount
            for (k in 0 until count) {
                val childView = getChildAt(k)

                if (childView.tag == removalTag)//更新的过程中避免对要移除的view更新
                    continue
                val speed = childView.getTag(R.string.KEY_SPEED) as Float
                val isUp = childView.getTag(R.string.KEY_DIRECTION) as Int == 0
                if (isUp) {
                    childView.y -= speed
                } else childView.y += speed
                val amplitude = childView.y - childView.top
                if (Math.abs(amplitude) > AMPLITUDE)
                    reverseDirection(childView, isUp)
            }
        }

        private fun reverseDirection(childView: View, up: Boolean) {
            if (up) childView.setTag(R.string.KEY_DIRECTION, 1)
            else childView.setTag(R.string.KEY_DIRECTION, 0)
        }
    }

    private inner class ViewClick : OnClickListener {
        override fun onClick(v: View) {
            val id = v.getTag(R.string.KEY_ID).toString()
            listener.onDiamondClick(this@ShuffleLayout, id, v)
        }

    }

    interface OnDiamondClickListener {
        fun onDiamondClick(shuffleLayout: ShuffleLayout, id: String, diamondView: View)
    }

    interface OnDiamondEmptyListener {
        //钻石被收取完毕，给外部一次机会，显示信息
        fun onDiamondEmpty(): String
    }

    lateinit var listener: OnDiamondClickListener

     var emptyListener: OnDiamondEmptyListener? =null

    /**
     * animate view out and remove it
     */
    fun expel(v: View) {
        v.tag = removalTag
        //由于我们是通过setX setY方式设置的坐标，onLayout都是layout(0,0,x,x)所以不能依照top因为top是0
        val duration = Math.max((v.top / measuredHeight.toFloat() * 550).toLong(), 200)
        views.remove(v)
        v.animate()
                .translationY((-(v.top + v.height)).toFloat())
                .alpha(0f)
                .setDuration(duration)
                .setInterpolator(AccelerateInterpolator())
                .setListener(object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator?) {
                        holes.add(Point(v.getTag(R.string.KEY_POS_LEFT) as Int, v.getTag(R.string.KEY_POS_TOP) as Int))
                        removeView(v)
                        if (isEmpty()) {
                            clear()
                            setHintWhenEmpty()
                        } else
                            reload()
                    }
                })
                .start()

    }

    private fun clear() {
        views.clear()
        datas.clear()
        rest.clear()
    }

    /**
     * 钻石 收取完毕后 应该显示什么 hint,优先
     */
    fun setHintWhenEmpty() {
        if (isEmpty())
            setHintDiamond(
                    if (TextUtils.isEmpty(emptyListener?.onDiamondEmpty()))
                        defaultEmptyHint
                    else
                        emptyListener?.onDiamondEmpty()
                                ?:defaultEmptyHint
            )
    }

    /**
     * 是否已经收集完毕
     */
    fun isEmpty(): Boolean {
        return views.isEmpty()
    }


    /**
     * 当移除view后，此方法用于补充view
     */
    private fun reload() {
        if (rest.isEmpty())
            return
        val profit = rest.removeAt(0)
        val itemView = ItemView(context)
        addView(itemView)
        bindViewData(itemView, profit)
        measureChild(itemView, 0, 0)
        (itemView.parent as ViewGroup).removeView(itemView)
        positionView(itemView)
        showView(itemView)
    }

    private fun showView(itemView: ItemView) {
        itemView.alpha = 0f
        itemView.animate()
                .alpha(1f)
                .setDuration(350)
                .start()
    }

    var extraView: View? = null

    var extraClickListener: View.OnClickListener? = null
        set(value) {
            this.extraView?.apply { this.setOnClickListener(value) }
        }

    fun setExtra(extraView: View) {
        if (this.extraView != null) return //already set

        this.extraView = extraView
        this.extraView?.setTag(R.string.KEY_ID, "extra")//保存下标,为了方便确定点击了哪个
        this.extraView?.setTag(R.string.KEY_SPEED, (Random().nextFloat() + 0.2f))//设置一个初始的随机速度
        this.extraView?.setTag(R.string.KEY_DIRECTION, Random().nextInt(2))//设置一个随机运动方向
        this.extraView?.setOnClickListener(extraClickListener)

        if (extraView.parent != null) return
        addView(extraView)
        measureChild(extraView, 0, 0)
        this@ShuffleLayout.extraView?.layout(this@ShuffleLayout.measuredWidth - extraView.measuredWidth,
                18.dp,
                this@ShuffleLayout.measuredWidth, extraView.measuredHeight+18.dp)

    }


    //过滤重复的diamond
    fun setData(items: List<Diamond>) {
        if (oldDatas.isNotEmpty() && Arrays.equals(datas.toArray(), oldDatas.toArray())) {
            Toaster.message("趣钻重复异常,刷新一下")
            return
        }
        post {
            if (items.isEmpty()) return@post
            if (hintView != null) {
                removeView(hintView)
                views.remove(hintView!!)
            }
            //去除重复的diamond(这里去重是因为首页接口返回的diamond都是未领取的diamond，但是在ui上有些已经显示了)
            val elements = filter(items)
            if (elements.isEmpty()) return@post
            this.datas.addAll(elements)
            if (views.size >= maxCount)//如果当前已经显示了最大数的趣钻，则全部加入候补队列
                rest.addAll(elements)
            else {//显示最大数的趣砖，并把剩下的趣砖加入候补队列
                val countToAdd = maxCount - views.size
                val boundIndex = Math.min(countToAdd, elements.size)
                addItemView(elements.subList(0, boundIndex))
                rest.addAll(elements.subList(boundIndex, Math.max(elements.size, boundIndex)))
            }
            triggerLoop()
        }

    }

    private fun addItemView(subList: List<Diamond>) {
        subList.forEach {
            addItem(it)
        }
    }


    fun filter(items: List<Diamond>): List<Diamond> {
        val uniqueDiamonds = ArrayList<Diamond>()
        //如果没有显示，那么清空datas,用新的数据替换旧数据
        if (views.isEmpty()) {
            datas.clear()
            rest.clear()
            return items
        }
        items.forEach {
            if (!datas.contains(it)) {
                uniqueDiamonds.add(it)
            }
        }
        return uniqueDiamonds
    }

    private fun addItem(diamond: Diamond) {
        val itemView = ItemView(context)
        addView(itemView)
        //bindView必须在measure之前，否则文字不显示
        bindViewData(itemView, diamond)
        measureChild(itemView, 0, 0)
        (itemView.parent as ViewGroup).removeView(itemView)
        positionView(itemView)
    }

    private val holes = hashSetOf<Point>()
    private val extraHoles = hashSetOf<Point>()

    //fix diamond position
    private fun initHolesIfEmpty(itemWidth: Int, itemHeight: Int) {

        if (holes.isNotEmpty()) return

        if (measuredHeight <= 0 || measuredWidth <= 0) return

        if (fixMode) {
            fastCreateHoles()
            return
        }


        //低分辨率下的退避选择，固定位置
        val lowScreenResolution = measuredHeight.toFloat() / (itemHeight * 1.2f) <= 3.7f

        if (lowScreenResolution) {
            // 4*4 宫格，随机抽取maxCount个格子
            val visited = hashSetOf<Int>()
            var maxRow = (measuredHeight / (itemHeight * 1.1f)).toInt()
            var maxColumn = (measuredWidth / (itemWidth * 1.1f)).toInt()

            if (maxRow * maxColumn < maxCount) {
                maxRow = 4
                maxColumn = 4
            }

            val gridCount = maxRow * maxColumn

            //占据 extraView （右上角）的位置，留给 ExtraView
            visited.add(maxColumn - 1)

            val startLeft = (measuredWidth - maxColumn * 1.1f * itemWidth).toInt() / 2
            val startTop = (measuredHeight - maxRow * 1.1f * itemHeight).toInt() / 2
            while (holes.size < maxCount) {
                val pos = random.nextInt(gridCount)

                if (visited.contains(pos)) continue

                val row = pos / maxColumn
                val column = pos % maxColumn

                //在左右一定范围内摆动
                val left = startLeft + column * itemWidth * 1.1f + (random.nextInt(itemWidth / 6 * 2) - itemWidth / 6)

                val top = startTop + row * itemHeight * 1.1f

                holes.add(Point(left.toInt(), top.toInt()))

                visited.add(pos)
            }

        } else {

            extraHoles.clear()
            extraHoles.add(Point((measuredWidth - (extraView?.measuredWidth
                    ?: itemWidth) * 1.3).toInt(), 0))

            //包括 extraView 的位置也要加入碰撞检测
            val allHoles = hashSetOf<Point>()
            allHoles.addAll(extraHoles)

            while (holes.size < maxCount) {

                if (measuredWidth - itemWidth <= 0 || measuredHeight - itemHeight <= 0) break
                val left = random.nextInt(measuredWidth - itemWidth)
                val top = random.nextInt(measuredHeight - itemHeight)
                if (intersectEdge(left, top, left + itemWidth, top + itemHeight)) continue
                val right = left + itemWidth
                val bottom = top + itemHeight
                var success = true
                for (item in allHoles) {
                    if (hasCollided(left - horizontalSpace, top - verticalSpace, right + horizontalSpace, bottom + verticalSpace,
                                    item.x, item.y, item.x + itemWidth, item.y + itemHeight)) {
                        success = false
                        break
                    }
                }

                if (success) {
                    val point = Point(left, top)
                    holes.add(point)
                    allHoles.add(point)
                }
            }
            allHoles.clear()
        }


    }

    private fun fastCreateHoles() {
        val w = measuredWidth
        val h = measuredHeight
        holes.add(Point((w * 0.55f).toInt(), (h * 0.2f).toInt()))
        holes.add(Point((w * 0.31f).toInt(), (h * 0.32f).toInt()))
        holes.add(Point((w * 0.71f).toInt(), (h * 0.34f).toInt()))
        holes.add(Point((w * 0.50f).toInt(), (h * 0.60f).toInt()))
        holes.add(Point((w * 0.41f).toInt(), (h * 0.72f).toInt()))
        holes.add(Point((w * 0.65f).toInt(), (h * 0.77f).toInt()))
    }


    private fun bindViewData(itemView: ItemView, profit: Diamond) {
        itemView.setDiamondProfit(profit.carat)
        itemView.setTag(R.string.KEY_ID, profit.id)//保存id,为了方便确定点击了哪个
        itemView.setTag(R.string.KEY_SPEED, (Random().nextFloat() + 0.2f))//设置一个初始的随机速度
        itemView.setTag(R.string.KEY_DIRECTION, Random().nextInt(2))//设置一个随机运动方向
        itemView.setOnClickListener(ViewClick())
    }

    private fun positionView(itemView: ItemView) {
        val itemWidth = itemView.measuredWidth
        val itemHeight = itemView.measuredHeight

        initHolesIfEmpty(itemWidth, itemHeight)

        if (holes.isEmpty()) return
        val xy = holes.first()
        holes.remove(xy)
        addView(itemView)
        val left = xy.x - itemWidth / 2
        val top = xy.y - itemHeight / 2
        itemView.setTag(R.string.KEY_POS_LEFT, xy.x)
        itemView.setTag(R.string.KEY_POS_TOP, xy.y)
        itemView.layout(left, top, left + itemWidth, top + itemHeight)
        views.add(itemView)

    }

    private fun intersectEdge(left: Int, top: Int, right: Int, bottom: Int): Boolean {
        return left < EDGE || top < EDGE || right > measuredWidth - EDGE || bottom > measuredHeight - EDGE
    }

    /**
     * 碰撞检测
     */
    private fun hasCollided(left: Int, top: Int, right: Int, bottom: Int,
                            targetLeft: Int, targetTop: Int, targetRight: Int, targetBottom: Int): Boolean {
        val testRectF = Rect(left, top, right, bottom)
        return testRectF.intersect(Rect(targetLeft, targetTop, targetRight, targetBottom)) || collideExtra(right - left, bottom - top, testRectF)

    }

    private fun collideExtra(w: Int, h: Int, testRectF: Rect): Boolean {
        return testRectF.intersect(Rect(
                measuredWidth - (extraView?.measuredWidth ?: w),
                0,
                measuredWidth,
                (extraView?.measuredHeight ?: h) + 20))
    }

    private fun triggerLoop() {
        if (isLooping)
            return
        mHandler.sendEmptyMessage(0)
        isLooping = true
    }


    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        /*no op*/
    }

    private var hintView: ItemView? = null
    //设置特殊的diamond，比如登录状态的，或者收集完毕显示正在生长的
    fun setHintDiamond(hint: String) {

        post {
            removeAllViews()
            datas.clear()
            rest.clear()
            views.clear()
            val itemView = ItemView(context)
            hintView = itemView
            addView(itemView)
            bindViewData(itemView, Diamond("", "", hint, ""))
            measureChild(itemView, 0, 0)
            val itemWidth = itemView.measuredWidth
            val itemHeight = itemView.measuredHeight
            val left = measuredWidth / 2 - itemWidth / 2
            val top = measuredHeight / 2 - itemHeight / 2
            itemView.layout(left, top, left + itemWidth, top + itemHeight)
//            views.add(itemView)

            this.extraView?.apply {
                if (this.parent != null) return@apply
                if (extraHoles.isEmpty())
                    extraHoles.add(Point(this@ShuffleLayout.measuredWidth - this.measuredWidth, 0))
                addView(extraView)
                measureChild(extraView, 0, 0)
                this@ShuffleLayout.extraView?.layout(this@ShuffleLayout.measuredWidth - this.measuredWidth,
                        0,
                        this@ShuffleLayout.measuredWidth, this.measuredHeight)
            }
            triggerLoop()
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        if (hintView != null) {
            val itemWidth = hintView!!.measuredWidth
            val itemHeight = hintView!!.measuredHeight
            val left = measuredWidth / 2 - itemWidth / 2
            val top = measuredHeight / 2 - itemHeight / 2
            hintView!!.layout(left, top, left + itemWidth, top + itemHeight)
        }

        if (extraView != null) {
            val itemWidth = extraView!!.measuredWidth
            val itemHeight = extraView!!.measuredHeight
            val left = measuredWidth - itemWidth
            val top = 0
            extraView!!.layout(left, top, left + itemWidth, top + itemHeight)
        }

        if (oldw != w || oldh != h) extraView?.requestLayout()
    }

    override fun onSaveInstanceState(): Parcelable? {
        return super.onSaveInstanceState()
    }

    override fun onRestoreInstanceState(state: Parcelable?) {
        super.onRestoreInstanceState(state)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mHandler.removeCallbacksAndMessages(null)
    }
}


